/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// type::null_t, type::empty_t
#include "type/typetools.h"
// nx_types
#include "type/typelist.h"
// nx_traits
#include "type/typetraits.h"
// nx::swap
#include "algorithm/algorithm.h"
// nx_assert_static
#include "bugfix/assert.h"
// Preprocessor
#include "macro/preprocessor.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

/*
    detail
*/

namespace private_
{
    template <typename T, typename U = type::null_t>
    struct TupleList_ : type::List<T, U>
    {
        typedef type::List<T, U> base_t;

        typename base_t::head_t head_;
        typename base_t::tail_t tail_;

        TupleList_(void) {}

        template <NX_PP_TYPE_MAX(typename P)>
        TupleList_(NX_PP_PARAM_MAX(P, & par))
            : head_(par1)
            , tail_(NX_ARGS_L1(NX_PP_TYPE_MAX(par)), type::none)
        {}

        template <typename T_, typename U_>
        TupleList_(const TupleList_<T_, U_>& r)
            : head_(r.head_)
            , tail_(r.tail_)
        {}

        template <typename T_, typename U_>
        TupleList_& operator=(const TupleList_<T_, U_>& r)
        {
            head_ = r.head_;
            tail_ = r.tail_;
            return (*this);
        }

        template <typename T_, typename U_>
        TupleList_& swap(const TupleList_<T_, U_>& r)
        {
            nx::swap(head_, r.head_);
            tail_.swap(r.tail_);
            return (*this);
        }
    };

    template <typename T>
    struct TupleList_<T, type::null_t> : type::List<T, type::null_t>
    {
        typedef type::List<T, type::null_t> base_t;

        typename base_t::head_t head_;

        TupleList_(void) {}

        template <NX_PP_TYPE_MAX(typename P)>
        TupleList_(P1& par1, NX_ARGS_L1(NX_PP_TYPE_MAX(P, &)))
            : head_(par1)
        {}

        template <typename T_>
        TupleList_(const TupleList_<T_, type::null_t>& r)
            : head_(r.head_)
        {}

        template <typename T_>
        TupleList_& operator=(const TupleList_<T_, type::null_t>& r)
        {
            head_ = r.head_;
            return (*this);
        }

        template <typename T_>
        TupleList_& swap(const TupleList_<T_, type::null_t>& r)
        {
            nx::swap(head_, r.head_);
            return (*this);
        }
    };
}

/*
    tuple for values
*/

template <NX_PP_TYPE_MAX(typename T, = type::null_t)>
class tuple : public nx_types_seq(private_::TupleList_<T1>, NX_ARGS_L1(NX_PP_TYPE_MAX(T)))
{
public:
    typedef typename nx_types_seq(private_::TupleList_<T1>, NX_ARGS_L1(NX_PP_TYPE_MAX(T))) base_t;

public:
    tuple(void) {}

#define NX_TUPLE_(i) \
    tuple(NX_PP_PARAM(i, typename Traits<T, >::param_t par)) \
        : base_t(NX_PP_TYPE(i, par), NX_PP_REPEAT(NX_PP_REM(i), type::none)) \
    {}

    NX_PP_MUL(NX_DEC(NX_MACRO_MAX), NX_TUPLE_)

#undef NX_TUPLE_

    tuple(NX_PP_PARAM_MAX(typename Traits<T, >::param_t par))
        : base_t(NX_PP_TYPE_MAX(par))
    {}

    template<NX_PP_TYPE_MAX(typename U)>
    tuple(const tuple<NX_PP_TYPE_MAX(U)>& r)
        : base_t(r)
    {}

    template<class U1, class U2>
    tuple& operator=(const private_::TupleList_<U1, U2>& r)
    {
        base_t::operator=(r);
        return (*this);
    }

    template <class U1, class U2>
    tuple& operator=(const std::pair<U1, U2>& r)
    {
        nx_assert_static(nx_types_len(tuple) == 2);
        this->head_       = r.first;
        this->tail_.head_ = r.second;
        return (*this);
    }

    template <class U1, class U2>
    tuple& swap(const private_::TupleList_<U1, U2>& r)
    {
        base_t::swap(r);
        return (*this);
    }

    size_t length(void)
    {
        return nx_types_len(tuple);
    }
    
    /*
        For getting a value from this tuple
    */
protected:
    template <int N, typename R>
    struct detail_
    {
        template <class U1, class U2>
        static R _(private_::TupleList_<U1, U2>& t)
        {
            return detail_<N - 1, R>::_(t.tail_);
        }
    };

    template <typename R>
    struct detail_<0, R>
    {
        template <class U1, class U2>
        static R _(private_::TupleList_<U1, U2>& t)
        {
            return t.head_;
        }
    };

public:
    template <int N>
    typename Traits<typename nx_types_at(tuple, N)>::param_t _(void)
    {
        return detail_<N, typename Traits<typename nx_types_at(tuple, N)>::param_t>::_(*this);
    }

    typename Traits<typename nx_types_at(tuple, 0)>::param_t operator*(void)
    {
        return _<0>();
    }
};

/*
    Special swap algorithm
*/

template
<
    NX_PP_TYPE_MAX(typename T), 
    NX_PP_TYPE_MAX(typename U)
>
void swap(tuple<NX_PP_TYPE_MAX(T)>& x, 
          tuple<NX_PP_TYPE_MAX(U)>& y)
{
    x.swap(y);
}

/*
    Special list algorithms
*/

#define NX_TYPENAME_(i) , typename
NX_SPECIAL_TYPES(tuple, typename NX_PP_MUL(NX_DEC(NX_MACRO_MAX), NX_TYPENAME_))
#undef NX_TYPENAME_

/*
    Tie variables to be a tuple
*/

#define NX_TIE_(i) \
template <NX_PP_TYPE(i, typename T)> \
inline tuple<NX_PP_TYPE(i, T, &)> tie(NX_PP_PARAM(i, T, & par)) \
{ \
    return tuple<NX_PP_TYPE(i, T, &)>(NX_PP_TYPE(i, par)); \
}

NX_PP_MAX(NX_TIE_)

#undef NX_TIE_

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
